﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Antlr.StringTemplate;
using System.Collections;
using System.Data;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.xml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml;
using www.serviciipeweb.ro.iafblog.ExportHierarchical;

namespace www.serviciipeweb.ro.iafblog.ExportDLL
{


    /// <summary>
    /// Name of formats
    /// </summary>
    public enum ExportToFormat
    {
        /// <summary>
        /// word 2003 xml
        /// </summary>
        Word2003XML,
        /// <summary>
        /// excel 2003 xml
        /// </summary>
        Excel2003XML,
        /// <summary>
        /// csv
        /// </summary>
        CSV,
        /// <summary>
        /// html
        /// </summary>
        HTML,
        /// <summary>
        /// xml
        /// </summary>
        XML,
        /// <summary>
        /// itexsharp xml
        /// </summary>
        itextSharpXML,
        /// <summary>
        /// PDF from itextSharpXML
        /// </summary>
        PDFtextSharpXML,
        /// <summary>
        /// word 2007
        /// </summary>
        Word2007,
        /// <summary>
        /// excel 2007
        /// </summary>
        Excel2007
    }
    /// <summary>
    /// just an internal class for ST_NODE creation
    /// </summary>
    class Fake
    {
    }


    /// <summary>
    /// what props to export from object
    /// </summary>
    public class ExportProps
    {

        /// <summary>
        /// Gets or sets the name prop.
        /// </summary>
        public string NameProp { get; set; }

        /// <summary>
        /// Gets or sets the display name prop.
        /// </summary>        
        public string DisplayNameProp { get; set; }

        /// <summary>
        /// Gets or sets the format - how to display
        /// </summary>
        public string Format { get; set; }



    }
    /// <summary>
    /// extension methods for IENUMERABLE AND DATATABLE
    /// </summary>
    public static class CollectionRenderProperties
    {

        /// <summary>
        /// internal caching the methods for each type
        /// </summary>
        private static Dictionary<string, Dictionary<string, MethodInfo>> methods;


        /// <summary>
        /// internal caching properties for each type
        /// </summary>
        private static Dictionary<string, List<string>> nameprops;


        /// <summary>
        /// Initializes the properties for the type and populates the cache
        /// </summary>
        /// <param name="t">The type</param>
        private static void InitializeProperties(Type t)
        {
            //TODO:thread safe
            if (methods == null)
            {
                methods = new Dictionary<string, Dictionary<string, MethodInfo>>();
                nameprops = new Dictionary<string, List<string>>();
            }

            if (methods.ContainsKey(t.FullName))
                return;


            Dictionary<string, MethodInfo> mi = new Dictionary<string, MethodInfo>();
            List<string> props = new List<string>();
            foreach (PropertyInfo pi in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {

                if (pi.GetGetMethod() == null)
                    continue;


                if (pi.GetGetMethod().GetParameters().Length > 0)
                    continue;

                props.Add(pi.Name);
                mi.Add(pi.Name, pi.GetGetMethod());
            }
            methods.Add(t.FullName, mi);
            nameprops.Add(t.FullName, props);

        }
        /// <summary>
        /// helper method - if you know the type
        /// </summary>
        /// <typeparam name="T">type to </typeparam>
        /// <param name="col">collection to be exported</param>
        /// <param name="exp">the format to export</param>
        /// <param name="FileName">Name file to be written </param>
        public static void ExportTo<T>(this IEnumerable<T> col, ExportToFormat exp, string FileName)
            where T : class
        {
            ExportTo(col, typeof(T), exp, FileName);
        }


        /// <summary>
        /// Properties from object.
        /// </summary>
        /// <param name="type">The type to retrieve all properties</param>
        /// <returns></returns>
        private static List<ExportProps> PropsFromObject(Type type)
        {
            List<ExportProps> props = new List<ExportProps>();

            foreach (string s in nameprops[type.FullName])
            {
                ExportProps exp = new ExportProps() { NameProp = s, DisplayNameProp = s };
                props.Add(exp);
            }
            return props;
        }

        /// <summary>
        /// helper method to export all properties.
        /// </summary>
        /// <param name="col">The collection</param>
        /// <param name="type">The type to be exported</param>
        /// <param name="exp">The export format</param>
        /// <param name="FileName">Name of the file to be written</param>
        public static void ExportTo(this IEnumerable col, Type type, ExportToFormat exp, string FileName)
        {
            InitializeProperties(type);
            ExportTo(col, type, PropsFromObject(type), exp, FileName);


        }

        /// <summary>
        /// helper method to create excel .
        /// </summary>
        /// <param name="Sheet1Text">The text of sheet1</param>
        /// <param name="DestinationFile">The destination file.</param>
        private static void CreateExcel2007(string Sheet1Text, string DestinationFile)
        {

            using (SpreadsheetDocument sd = SpreadsheetDocument.Create(DestinationFile, SpreadsheetDocumentType.Workbook))
            {
                WorkbookPart workbook = sd.AddWorkbookPart();
                WorksheetPart sheet = workbook.AddNewPart<WorksheetPart>();
                WriteToPart(sheet, Sheet1Text);
                //TODO :put into a string on the fule
                WriteToPart(workbook, string.Format(
                    "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"><sheets><sheet name=\"{1}\" sheetId=\"1\" r:id=\"{0}\" /></sheets></workbook>", workbook.GetIdOfPart(sheet), "Sheet1"));

                sd.Close();
            }
        }

        /// <summary>
        /// INodes from the specified col.
        /// </summary>
        /// <param name="col">The collection of objects</param>
        /// <param name="type">The type of objects</param>
        /// <param name="properties">The properties of objects to be exported </param>        
        /// <returns>INode to be serialized</returns>
        private static INode Node(IEnumerable col, Type type, List<ExportProps> properties)
        {
            INode st = new ST_Node<Fake>(new Fake(), null, null);

            st.Name = "Collection";
            st.UniqueID = st.Name;

            InitializeProperties(type);

            Dictionary<string, MethodInfo> mi = methods[type.FullName];

            HashTableAddRecursiveObjects obj = new HashTableAddRecursiveObjects();

            foreach (ExportProps exp in properties)
            {

                st.Properties.Add(exp.NameProp, exp.DisplayNameProp);
                
            }
            int i = 0;
            foreach (object t in col)
            {
                if (t == null)
                    continue;


                INode du = GenReflectNode.CreateExporter(t, null, null) as INode;

                foreach (ExportProps exp in properties)
                {
                    object o = mi[exp.NameProp].Invoke(t, null);

                    if (o != null)
                    {
                        string value = null;
                        if (exp.Format != null)
                        {
                            value = string.Format("{0:" + exp.Format + "}", o);
                        }
                        else
                        {
                            value = o.ToString();
                        }

                        du.Properties.Add(exp.NameProp, value);
                    }
                    else
                        du.Properties.Add(exp.NameProp, "");
                }

                obj.Add("K" + (i++), du);
            }
            st.SubNodes.Add("A", obj);


            return st;
        }
        private static string _path;
        /// <summary>
        /// Todo :use this with Server.MapPath(".") and put
        /// the templates folder in the root of your web files
        /// </summary>
        public static string PathFiles
        {
            get
            {
                return _path;
            }
            set
            {
                _path = value;
            }
        }

        /// <summary>
        /// Gets the StringTemplateGroup  collection.
        /// </summary>        
        private static StringTemplateGroup stgCollection
        {
            get
            {
                string where = @"templates\Collection";
                if (!string.IsNullOrEmpty(PathFiles))
                {
                    where = Path.Combine(PathFiles, where);
                }
                StringTemplateGroup stg = new StringTemplateGroup("Collection", where);
                stg.RegisterAttributeRenderer(typeof(string), new AdvancedStringRenderer());
                stg.RegisterAttributeRenderer(typeof(DateTime), new AdvancedDateTimeRenderer());
                return stg;
            }
        }

        /// <summary>
        /// Creates the PDF from itextsharp xml
        /// uses temporary folder
        /// </summary>
        /// <param name="ItextSharpXML">The itext sharp XML.</param>
        /// <param name="FileName">Name of the file.</param>
        private static void CreateFilePDF(string ItextSharpXML, string FileName)
        {
            string TempFile = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetTempFileName()));
            File.WriteAllText(TempFile + ".xml", ItextSharpXML);
            Document document = new Document();
            PdfWriter.GetInstance(document, new FileStream(FileName, FileMode.Create));
            XmlParser.Parse(document, TempFile + ".xml");
            document.Close();
        }

        /// <summary>
        /// Writes to part - helper method to fill a part from a string
        /// </summary>
        /// <param name="oxp">The OpenXmlPart </param>
        /// <param name="Text">The text.</param>
        private static void WriteToPart(OpenXmlPart oxp, string Text)
        {
            using (Stream stream = oxp.GetStream())
            {
                byte[] buf = (new UTF8Encoding()).GetBytes(Text);
                stream.Write(buf, 0, buf.Length);
            }
        }

        /// <summary>
        /// hemper method to create word2007 from a XML main text 
        /// </summary>
        /// <param name="Text">The text</param>
        /// <param name="FileName">Name of the file.</param>
        private static void CreateWord2007(string Text, string FileName)
        {
            using (WordprocessingDocument wordDoc = WordprocessingDocument.Create(FileName, WordprocessingDocumentType.Document))
            {
                // Set the content of the document so that Word can open it.
                MainDocumentPart mainPart = wordDoc.AddMainDocumentPart();
                WriteToPart(mainPart, Text);
                wordDoc.Close();
            }
        }


        /// <summary>
        /// Exports to a format the IEnumerable 
        /// </summary>
        /// <param name="col">The collection</param>
        /// <param name="type">The type of the objects</param>
        /// <param name="properties">The properties of the objects</param>
        /// <param name="exp">The export format</param>
        /// <param name="FileName">Name of the file.</param>
        public static void ExportTo(this IEnumerable col, Type type, List<ExportProps> properties, ExportToFormat exp, string FileName)
        {
            InitializeProperties(type);

            string s = Template2String(col, type, properties, exp);
            switch (exp)
            {
                case ExportToFormat.PDFtextSharpXML:
                    CreateFilePDF(s, FileName);
                    break;
                case ExportToFormat.Word2007:
                    CreateWord2007(s, FileName);
                    break;
                case ExportToFormat.Excel2007:
                    CreateExcel2007(s, FileName);
                    break;
                default:
                    File.WriteAllText(FileName, s);
                    break;

            }


        }
        /// <summary>
        /// helper method to export a data table
        /// </summary>
        /// <param name="dt">The datatable</param>
        /// <param name="exp">The export format</param>
        /// <param name="FileName">Name of the file.</param>
        public static void ExportTo(this DataTable dt, ExportToFormat exp, string FileName)
        {

            ExportTo(dt, PropsFromObject(dt), exp, FileName);


        }
        /// <summary>
        /// Properties from data table.
        /// </summary>
        /// <param name="dt">The datatable to be exported</param>
        /// <returns>columns as properties</returns>
        private static List<ExportProps> PropsFromObject(DataTable dt)
        {
            List<ExportProps> props = new List<ExportProps>();

            foreach (DataColumn dc in dt.Columns)
            {
                ExportProps exp = new ExportProps() { NameProp = dc.ColumnName, DisplayNameProp = dc.ColumnName };
                props.Add(exp);
            }
            return props;
        }
        /// <summary>
        /// Exports to a specific format
        /// </summary>
        /// <param name="dt">The data table</param>
        /// <param name="properties">The properties to be exported</param>
        /// <param name="exp">The export format</param>
        /// <param name="FileName">Name of the file.</param>
        public static void ExportTo(this DataTable dt, List<ExportProps> properties, ExportToFormat exp, string FileName)
        {

            string s = DataTable2String(dt, properties, exp);
            switch (exp)
            {
                case ExportToFormat.PDFtextSharpXML:
                    CreateFilePDF(s, FileName);
                    break;
                case ExportToFormat.Word2007:
                    CreateWord2007(s, FileName);
                    break;
                case ExportToFormat.Excel2007:
                    CreateExcel2007(s, FileName);
                    break;
                default:
                    File.WriteAllText(FileName, s);
                    break;
            }

        }

        /// <summary>
        /// Template2s the string.
        /// </summary>
        /// <param name="col">The col.</param>
        /// <param name="type">The type.</param>
        /// <param name="properties">The properties.</param>
        /// <param name="exp">The exp.</param>
        /// <returns></returns>
        private static string Template2String(IEnumerable col, Type type, List<ExportProps> properties, ExportToFormat exp)
        {
            string Template = null;
            switch (exp)
            {
                case ExportToFormat.PDFtextSharpXML:
                    Template = ExportToFormat.itextSharpXML.ToString();
                    break;
                default:
                    Template = exp.ToString();
                    break;
            }
            StringTemplate stNeed = stgCollection.GetInstanceOf(Template);
            stNeed.SetAttribute("DateCreated", DateTime.Now);
            INode st = Node(col, type, properties);
            stNeed.SetAttribute("ItemToDisplay", st);
            return stNeed.ToString();

        }


        /// <summary>
        /// Exports the Datatable to a string.
        /// </summary>
        /// <param name="dt">The datatable</param>
        /// <param name="properties">The properties to be exported</param>
        /// <param name="exp">The export format</param>
        /// <returns></returns>
        private static string DataTable2String(DataTable dt, List<ExportProps> properties, ExportToFormat exp)
        {
            string Template = null;
            switch (exp)
            {
                case ExportToFormat.PDFtextSharpXML:
                    Template = ExportToFormat.itextSharpXML.ToString();
                    break;
                default:
                    Template = exp.ToString();
                    break;
            }
            StringTemplate stNeed = stgCollection.GetInstanceOf(Template);
            stNeed.SetAttribute("DateCreated", DateTime.Now);
            INode st = NodeFromDataTable(dt, properties);
            stNeed.SetAttribute("ItemToDisplay", st);
            return stNeed.ToString();

        }
        /// <summary>
        /// Makes a INode from data table.
        /// </summary>
        /// <param name="dt">The datatable.</param>
        /// <param name="properties">The properties.</param>
        /// <returns>ST_NODE</returns>
        private static INode NodeFromDataTable(DataTable dt, List<ExportProps> properties)
        {
            INode st = new ST_Node<Fake>(new Fake(), null, null);

            st.Name = "DataTable";
            st.UniqueID = st.Name;



            HashTableAddRecursiveObjects obj = new HashTableAddRecursiveObjects();
            foreach (ExportProps exp in properties)
            {
                st.Properties.Add(exp.NameProp, exp.DisplayNameProp);

            }
            int i = 0;

            foreach (DataRow dr in dt.Rows)
            {

                INode du = new ST_Node<DataRow>(dr, null, null);

                foreach (ExportProps exp in properties)
                {

                    object o = dr[exp.NameProp];

                    if (o != null && o != DBNull.Value)
                    {
                        string value = null;
                        if (exp.Format != null)
                        {
                            value = string.Format("{0:" + exp.Format + "}", o);
                        }
                        else
                        {
                            value = o.ToString();
                        }

                        du.Properties.Add(exp.NameProp, value);
                    }
                    else
                        du.Properties.Add(exp.NameProp, "");
                }

                obj.Add("K" + i, du);
                i++;
            }
            st.SubNodes.Add("A", obj);


            return st;
        }







    }
}